package com.cartoonrabbit.service;


import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.cartoonrabbit.config.exception.ServiceException;
import com.cartoonrabbit.config.redis.constant.CacheConstants;
import com.cartoonrabbit.config.redis.utils.RedisCache;
import com.cartoonrabbit.config.security.pojo.LoginUser;
import com.cartoonrabbit.config.security.utils.JwtTokenUtil;
import com.cartoonrabbit.pojo.User;
import com.cartoonrabbit.vo.AuthPhoneNumberParam;
import com.cartoonrabbit.vo.LoginParam;
import com.cartoonrabbit.wxapp.properties.WxAppInfoProperties;
import com.cartoonrabbit.wxapp.properties.WxAppJwtProperties;
import com.cartoonrabbit.wxapp.properties.WxAppRequestInfoProperties;
import com.cartoonrabbit.wxapp.utils.WxAppUtils;
import com.cartoonrabbit.wxapp.utils.WxappRequestContextPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.concurrent.TimeUnit;

/**
 * 授权业务类
 */

@Service
public class AuthService {
    private static final Logger log = LoggerFactory.getLogger(AuthService.class);

    @Autowired
    private WxAppInfoProperties wxAppInfoProperties;

    @Autowired
    private WxAppRequestInfoProperties wxAppRequestInfoProperties;

    @Autowired
    private IUserService userService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private RedisCache redisCache;
    @Autowired
    private IInviteRecordService inviteRecordService;

    @Autowired
    private WxAppJwtProperties wxAppJwtProperties;

    public String login(LoginParam loginParam){
        // 判断是否含有手机号
        if (ObjectUtils.isEmpty(loginParam.getPhone())){
            throw new ServiceException("请先授权手机号",HttpStatus.HTTP_FORBIDDEN);
        }

        // 登录用户
        User doLoginUser=null;
        //      1.先根据js_code去微信服务器拿到session_key
        String url = String.format(wxAppRequestInfoProperties.getBaseUrl() + WxappRequestContextPath.AUTH_CODE_SESSION,
                wxAppInfoProperties.getAppId(), wxAppInfoProperties.getAppSecret(), loginParam.getJsCode());

        HttpRequest post = HttpUtil.createPost(url);

        HttpResponse response = post.execute();
        String body = response.body();
        JSONObject appidSessionObject = JSONUtil.parseObj(body);
        // 获取到session_key
        String session_key = appidSessionObject.getStr("session_key");
        // 获取到openid
        String openid = appidSessionObject.getStr("openid");

        // 判断用户信息是否被篡改
        String phoneNumber = (String) redisCache.getCacheObject(CacheConstants.PHONE_OPENID_KEY + openid);
        if (!phoneNumber.equals(loginParam.getPhone())){
            throw new ServiceException("用户手机号被串改，请重新登录",HttpStatus.HTTP_FORBIDDEN);
        }
        redisCache.deleteObject(CacheConstants.PHONE_OPENID_KEY+openid);

//        2.(此步可省略)使用 sha1( rawData + sessionkey )拿到字符串,判断与signature值是否相同,如果相同则用户信息无误,可进行下一步.如果不同,则说明用户信息被篡改或过期.
        // 获取sha1字符串
        String signature2=null;
        try {
            signature2 = WxAppUtils.getsignature2(loginParam.getRawData(), session_key);
        }catch (Exception e){
            throw new ServiceException("服务器异常，请稍后重试", HttpStatus.HTTP_INTERNAL_ERROR);
        }
        // 判断sha1Str是否与signature值是否相同
        if (!signature2.equals(loginParam.getSignature())){
            throw new ServiceException("授权信息被篡改或过期，请重新登录",HttpStatus.HTTP_INTERNAL_ERROR);
        }

        // 判断用户是否已经在数据库中
        doLoginUser  = userService.getUserInfoByOpenId(openid);
        if (ObjectUtils.isEmpty(doLoginUser)){// 为空进行数据库注册
            //        3.然后根据解密算法自行解密(输入参数为appId,sessionKey,encryptedData,iv,返回一个jsonObj),拿到openId和unionId等信息,执行服务器端的注册/登录操作.
            // 解密获取用户信息
            String userInfoStr = WxAppUtils.wxDecrypt(loginParam.getEncryptedData(), session_key, loginParam.getIv());
            // 将用户信息解析成json对象
            JSONObject userInfo = JSONUtil.parseObj(userInfoStr);
            // 封装成user对象，并判断是否已经注册过
            User user = new User();
            user.setOpenId(openid);
            user.setNickName(userInfo.getStr("nickName"));
            user.setPhone(phoneNumber);
            // 对该用户进行数据库保存操作
            doLoginUser = userService.saveUserInfo(user);
            if (!ObjectUtils.isEmpty(loginParam.getInviteId())){
                // todo 说明该用户是杯邀请的，算提成给邀请的用户
                inviteRecordService.createInviteRecord(openid,loginParam.getInviteId());
            }
        }

        if (ObjectUtils.isEmpty(doLoginUser)){
            throw new ServiceException("服务器正忙", HttpStatus.HTTP_INTERNAL_ERROR);
        }
        // 对用户信息进行生成token操作
        LoginUser loginUser = new LoginUser(doLoginUser);
        String token = jwtTokenUtil.generateToken(loginUser);
        //更新security对象
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        // 生成存储键
        String key = CacheConstants.LOGIN_TOKEN_KEY+RandomUtil.randomString(32);
        // 将token保存到redis缓存中
        redisCache.setCacheObject(key,token,wxAppJwtProperties.getExpiration(), TimeUnit.SECONDS);
        // 将token对应的key响应出去
        return key;
    }

    /**
     * 获取手机号
     * @param phoneNumberParam
     * @return
     */
    public Object doGetPhoneNumber(AuthPhoneNumberParam phoneNumberParam) {
        try {
            //      1.先根据js_code去微信服务器拿到session_key
             String url = String.format(wxAppRequestInfoProperties.getBaseUrl() + WxappRequestContextPath.AUTH_CODE_SESSION,
                    wxAppInfoProperties.getAppId(), wxAppInfoProperties.getAppSecret(), phoneNumberParam.getJsCode());

            HttpRequest post = HttpUtil.createPost(url);

            HttpResponse response = post.execute();
            String body = response.body();
            JSONObject appidSessionObject = JSONUtil.parseObj(body);
            // 获取到session_key
            String session_key = appidSessionObject.getStr("session_key");
            // 获取到openid
            String openid = appidSessionObject.getStr("openid");

            // 然后根据解密算法自行解密(输入参数为appId,sessionKey,encryptedData,iv,返回一个jsonObj),拿到openId和unionId等信息,执行服务器端的注册/登录操作.
            // 解密获取用户信息
            String phoneInfoStr = WxAppUtils.wxDecrypt(phoneNumberParam.getEncryptedData(), session_key, phoneNumberParam.getIv());
            JSONObject entries = JSONUtil.parseObj(phoneInfoStr);
            // 将电话信息进行保存
            redisCache.setCacheObject(CacheConstants.PHONE_OPENID_KEY+openid,entries.getStr("phoneNumber"),wxAppJwtProperties.getExpiration(),TimeUnit.SECONDS);
            return entries;
        }catch (Exception e){
            throw new  ServiceException("服务器异常，请稍后重试", HttpStatus.HTTP_INTERNAL_ERROR);
        }
    }

    /**
     * 获取用户信息
     * @param name
     * @return
     */
    public User getUserInfoByOpenId(String name) {
        try {
            User userInfoByOpenId = userService.getUserInfoByOpenId(name);
            if (ObjectUtils.isEmpty(userInfoByOpenId)){
                throw new ServiceException("该用户不存在",HttpStatus.HTTP_INTERNAL_ERROR);
            }
            return userInfoByOpenId;
        }catch (Exception e){
            log.error("数据库异常，错误信息:'{}'",e.getMessage());
            throw new ServiceException("数据库异常，请稍后重试",HttpStatus.HTTP_INTERNAL_ERROR);
        }
    }
}
